package pageroute

import (
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"gioui.org/font/gofont"
	"gioui.org/layout"
	"gioui.org/text"
	"gioui.org/unit"
	"gioui.org/widget"
	"gioui.org/widget/material"
	"gioui.org/x/component"
	"gioui.org/x/richtext"
	"git.sr.ht/~mil/transito/connmobroute"
	"git.sr.ht/~mil/transito/uicomponents/divider"
	"git.sr.ht/~mil/transito/uistate"
	"git.sr.ht/~mil/transito/utillinkhandler"
)

type PageRoute struct {
	UIState *uistate.UIState
	list    widget.List

	richtextFrom       richtext.InteractiveText
	richtextTo         richtext.InteractiveText
	richtextResultArea richtext.InteractiveText

	ResultMobrouteResponse *connmobroute.MobrouteResponse
	ResultMobrouteError    error

	clickableRoute widget.Clickable
	clickableSwap  widget.Clickable

	TimerStartMobroute          *time.Time
	checkboxTransfersTable      widget.Bool
	checkboxTransfersComputed   widget.Bool
	checkboxVerbose             widget.Bool
	textfieldMaxWalkMinutes     component.TextField
	textfieldMaxTripMinutes     component.TextField
	textfieldMaxTransferMinutes component.TextField
	textfieldMinTransferMinutes component.TextField
	textfieldWalkspeedKmHr      component.TextField
}

func Initialize(u *uistate.UIState) PageRoute {
	widget := PageRoute{
		UIState: u,

		list: widget.List{List: layout.List{Axis: layout.Vertical}},

		ResultMobrouteResponse: nil,
		ResultMobrouteError:    errors.New("Result will show here"),

		TimerStartMobroute:          nil,
		textfieldMaxWalkMinutes:     component.TextField{Editor: widget.Editor{Filter: "1234567890"}},
		textfieldMaxTripMinutes:     component.TextField{Editor: widget.Editor{Filter: "1234567890"}},
		textfieldMaxTransferMinutes: component.TextField{Editor: widget.Editor{Filter: "1234567890"}},
		textfieldMinTransferMinutes: component.TextField{Editor: widget.Editor{Filter: "1234567890"}},
		textfieldWalkspeedKmHr:      component.TextField{Editor: widget.Editor{Filter: "1234567890."}},

		checkboxTransfersTable:    widget.Bool{},
		checkboxTransfersComputed: widget.Bool{},
	}
	widget.UIState.Theme.Shaper = text.NewShaper(text.WithCollection(gofont.Collection()))
	// Defaults
	widget.textfieldMaxWalkMinutes.SetText("10")
	widget.textfieldMaxTripMinutes.SetText("240")
	widget.textfieldMinTransferMinutes.SetText("3")
	widget.textfieldMaxTransferMinutes.SetText("20")
	widget.textfieldWalkspeedKmHr.SetText("4.5")

	for _, tf := range []*component.TextField{
		&widget.textfieldMaxWalkMinutes,
		&widget.textfieldMaxTripMinutes,
		&widget.textfieldMinTransferMinutes,
		&widget.textfieldMaxTransferMinutes,
		&widget.textfieldWalkspeedKmHr,
	} {
		tf.MoveCaret(9999, 99999)
	}
	return widget
}

func (widget *PageRoute) Render(gtx layout.Context) layout.Dimensions {
	utillinkhandler.HandleLinkEvents(widget.UIState.Window, &widget.richtextResultArea)

	fromSwitchTabClicked := utillinkhandler.HandleLinkEvents(widget.UIState.Window, &widget.richtextFrom) == "tab1"
	toSwitchTabClicked := utillinkhandler.HandleLinkEvents(widget.UIState.Window, &widget.richtextTo) == "tab1"
	if fromSwitchTabClicked || toSwitchTabClicked {
		widget.UIState.TabSelected = 1
		widget.UIState.Window.Invalidate()
	}

	widgetBtnRoute := wgToWidget(widget, wgButtonRoute)
	widgetBtnSwap := func(gtx layout.Context) layout.Dimensions {
		return material.Button(
			widget.UIState.Theme,
			&widget.clickableSwap, "Swap",
		).Layout(gtx)
	}
	if widget.clickableSwap.Clicked() {
		swapCoords := widget.UIState.LocationFromCoords
		swapLabel := widget.UIState.LocationFromLabel
		widget.UIState.LocationFromLabel = widget.UIState.LocationToLabel
		widget.UIState.LocationFromCoords = widget.UIState.LocationToCoords
		widget.UIState.LocationToLabel = swapLabel
		widget.UIState.LocationToCoords = swapCoords
	}

	// Route params
	widgetTextfieldMaxWalkMinutes := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(15))
		widget.textfieldMaxWalkMinutes.SingleLine = true
		return widget.textfieldMaxWalkMinutes.Layout(gtx, widget.UIState.Theme, "Max Wᵐ")
	}
	widgetTextfieldMaxTripMinutes := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(15))
		widget.textfieldMaxTripMinutes.SingleLine = true
		return widget.textfieldMaxTripMinutes.Layout(gtx, widget.UIState.Theme, "Max Tᵐ")
	}
	widgetTextfieldMinTransferMinutes := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(15))
		widget.textfieldMinTransferMinutes.SingleLine = true
		return widget.textfieldMinTransferMinutes.Layout(gtx, widget.UIState.Theme, "Min Cᵐ")
	}
	widgetTextfieldMaxTransferMinutes := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(15))
		widget.textfieldMaxTransferMinutes.SingleLine = true
		return widget.textfieldMaxTransferMinutes.Layout(gtx, widget.UIState.Theme, "Max Cᵐ")
	}
	widgetTextfieldWalkspeedKmHr := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(15))
		widget.textfieldWalkspeedKmHr.SingleLine = true
		return widget.textfieldWalkspeedKmHr.Layout(gtx, widget.UIState.Theme, "Wᵏᵐʰʳ")
	}

	// Update result area with mobroute timer value when running
	if widget.TimerStartMobroute != nil {
		widget.ResultMobrouteError = errors.New(fmt.Sprintf(
			"%.1fs",
			time.Now().Sub(*widget.TimerStartMobroute).Seconds(),
		))
		widget.UIState.Window.Invalidate()
	}

	widgetRouteRowA := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(40))
		gtx.Constraints.Max.Y = gtx.Constraints.Min.Y
		return layout.Flex{}.Layout(gtx, layout.Rigid(dimensionsToWidget(layout.Flex{}.Layout(
			gtx,
			layout.Flexed(0.2, widgetTextfieldMaxWalkMinutes),
			layout.Flexed(0.2, widgetTextfieldMaxTripMinutes),
			layout.Flexed(0.2, widgetTextfieldMinTransferMinutes),
			layout.Flexed(0.2, widgetTextfieldMaxTransferMinutes),
			layout.Flexed(0.2, widgetTextfieldWalkspeedKmHr),
		))))
	}

	widgetRouteRowB := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(40))
		gtx.Constraints.Max.Y = gtx.Constraints.Min.Y
		return layout.Flex{}.Layout(gtx, layout.Rigid(dimensionsToWidget(layout.Flex{}.Layout(
			gtx,
			//layout.Flexed(0.2, material.CheckBox(widget.UIState.Theme, &widget.checkboxTransfersTable, "Ttbl").Layout),
			//layout.Flexed(0.2, material.CheckBox(widget.UIState.Theme, &widget.checkboxTransfersComputed, "Tcmp").Layout),
			//layout.Flexed(0.2, material.CheckBox(widget.UIState.Theme, &widget.checkboxVerbose, "Verbose").Layout),
			layout.Flexed(0.2, widgetBtnSwap),
			layout.Flexed(0.01, layout.Spacer{Width: unit.Dp(2)}.Layout),
			layout.Flexed(0.2, widgetBtnRoute),
		))))
	}

	widgetToFromSplit := func(gtx layout.Context) layout.Dimensions {
		gtx.Constraints.Min.Y = gtx.Dp(unit.Dp(50))
		//gtx.Constraints.Max.Y = gtx.Constraints.Min.Y

		defaultLabel := "Click above to set"

		fromSpans := []richtext.SpanStyle{}
		fromLabel := defaultLabel
		if widget.UIState.LocationFromLabel != "" {
			fromLabel = fmt.Sprintf(
				"%s\n\n([%.5f, %.5f](geo:%.5f,%.5f))",
				widget.UIState.LocationFromLabel,
				widget.UIState.LocationFromCoords[0],
				widget.UIState.LocationFromCoords[1],
				widget.UIState.LocationFromCoords[0],
				widget.UIState.LocationFromCoords[1],
			)
		}
		if s, err := widget.UIState.MarkdownRenderer.Render([]byte(fmt.Sprintf(
			"[**From:**](transito://tab1)\n\n%s",
			fromLabel,
		))); err == nil {
			fromSpans = s
		}

		toSpans := []richtext.SpanStyle{}
		toLabel := defaultLabel
		if widget.UIState.LocationToLabel != "" {
			toLabel = fmt.Sprintf(
				"%s\n\n([%.5f, %.5f](geo:%.5f,%.5f))",
				widget.UIState.LocationToLabel,
				widget.UIState.LocationToCoords[0],
				widget.UIState.LocationToCoords[1],
				widget.UIState.LocationToCoords[0],
				widget.UIState.LocationToCoords[1],
			)
		}
		if s, err := widget.UIState.MarkdownRenderer.Render([]byte(fmt.Sprintf(
			"[**To:**](transito://tab1)\n\n%s",
			toLabel,
		))); err == nil {
			toSpans = s
		}

		return layout.Flex{Axis: layout.Horizontal}.Layout(gtx,
			layout.Flexed(1, richtext.Text(&widget.richtextFrom, widget.UIState.Theme.Shaper, fromSpans...).Layout),
			layout.Flexed(0.01, layout.Spacer{Width: unit.Dp(2)}.Layout),
			layout.Flexed(1, richtext.Text(&widget.richtextTo, widget.UIState.Theme.Shaper, toSpans...).Layout),
		)
	}

	var widgetMarkdownResult func(gtx layout.Context) layout.Dimensions
	if spans, err := widget.UIState.MarkdownRenderer.Render([]byte(
		func() string {

			if widget.ResultMobrouteError != nil {
				return fmt.Sprintf("%v", widget.ResultMobrouteError)
			} else {
				textBytes, _ := json.MarshalIndent(widget.ResultMobrouteResponse, "", "  ")
				textResult := string(textBytes)
				return fmt.Sprintf(
					"[Map](%s)\n\n```\n%s\n```",
					*widget.ResultMobrouteResponse.MapURL,
					textResult,
				)
			}
		}(),
	)); err == nil {
		widgetMarkdownResult = richtext.Text(
			&widget.richtextResultArea, widget.UIState.Theme.Shaper, spans...,
		).Layout
	} else {
		widgetMarkdownResult = richtext.Text(
			&widget.richtextResultArea, widget.UIState.Theme.Shaper,
		).Layout
	}

	widgetsScroll := []layout.Widget{
		widgetToFromSplit,
		divider.Divider(widget.UIState.Theme).Layout,
		widgetRouteRowA,
		widgetRouteRowB,
		divider.Divider(widget.UIState.Theme).Layout,
		widgetMarkdownResult,
	}

	return material.List(widget.UIState.Theme, &widget.list).Layout(gtx, len(widgetsScroll), func(gtx layout.Context, i int) layout.Dimensions {
		return layout.UniformInset(unit.Dp(16)).Layout(gtx, widgetsScroll[i])
	})
}
